home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Almathera Ten Pack 3: CDPD 3
/
Almathera Ten on Ten - Disc 3: CDPD3.iso
/
scope
/
051-075
/
scopedisk57
/
ipc
/
ipc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-03-19
|
19KB
|
477 lines
/*******************************************************************
* *
* IPC.c *
* *
* Inter-Process-Communication Procedures *
* *
* Release 1.0 -- 1988 May 11 *
* *
* Copyright 1988 Peter Goodeve *
* *
* This source is freely distributable, and may be used freely *
* in any program, but its functionality should not be modified *
* without prior consultation with the author. (This is just to *
* prevent proliferation of incompatible variants. Don't be *
* inhibited from suggesting enhancements!) *
* *
*******************************************************************/
/********************************************************************
* *
* Synopsis of usage: *
* ======== *
* *
* Client: *
* *
* port = GetIPCPort(name); *
* or port = FindIPCPort(name) *
* ..... *
* msg = CreateIPCMsg(nitems); *
* ..... *
* PutIPCMsg(msg,port); *
* ..... *
* DeleteIPCMsg(msg); *
* ..... *
* DropIPCPort(port); *
* *
* Server: [standard Exec procedures in brackets] *
* *
* port = ServeIPCPort(name); *
* ..... *
* [WaitPort(port); or Wait(sigbits);] *
* ..... *
* [msg = GetMsg(port);] *
* ..... *
* [ReplyMsg(msg);] *
* ..... *
* ShutIPCPort(port); *
* <handle remaining messages on the port> *
* LeaveIPCPort(port); *
* *
* Misc.: *
* *
* UseIPCPort(port); *
* CheckIPCPort(port,flags); *
* *
* *
********************************************************************/
/********************************************************************
* *
* These procedures provide a mechanism for independent processes *
* to communicate with each other through "IPC ports". These are *
* similar to standard Exec Message Ports, except that they are *
* not "owned" by any of the processes; any one process can *
* declare itself a "server" (provided no other is currently *
* claiming this right), and thus becomes temporarily the handler *
* of messages passed to this port. If there is no server, any *
* attempt to send a message to a port will return failure, and *
* the client may take whatever action is appropriate. A client *
* may safely "Get" a pointer to a named port (even if there is no *
* server yet) and can rely on it remaining valid until it "Drops" *
* it again. (In contrast to Exec ports, which have no such *
* protection.) *
* *
* IPC Ports don't appear in the Exec named port list -- they have *
* their own. There is a single publicly available name, itself *
* actually a port on the public list (for reasons we'll see later) *
* to which is added some data structure including the IPC Port *
* list. *
* *
* These procedures are just one part of a fully developed system. *
* (Another is the IPCMessage structure itself -- see IPC.h and *
* elsewhere -- which is essentially independent of the IPC Port *
* mechanism.) They form a fairly self contained set, so they've *
* initially been written as a single source file, but there's no *
* reason they couldn't be split into smaller segments. Their *
* natural home is a resident library in any case, but that can *
* wait for the moment. *
* *
* Other modules will doubtless have to be added. One such is *
* "LoadIPCPort(name)" which, after doing a GetIPCPort(), will *
* check the returned port to see if it has a server; if not, it *
* will check to see if there is a "broker" process serving the *
* IPCBasePort, and if so will send a message to that asking for *
* the port to be "served". (The broker is essentially a server *
* like any other -- it will probably look up the port in a user *
* supplied list and load the specified server; more advanced *
* models may check to see if the server is on another machine, *
* for example.) *
* - - - - - - - - - - - *
* *
* This code has only been tested under Lattice 4.0. It is *
* intended to be compiler independent, but please check it *
* for problems. The code is not always optimized! *
* - - - - - - - - - - - *
* *
* %% The fundamental concept of keeping a use-count %% *
* %% on a port is due to Matt Dillon. Thanks Matt! %% *
* *
********************************************************************/
#include "IPCPorts.h"
/* this should be included first to suppress defaults in IPC.h */
/* other modules in processes using IPC will not usually need
this header -- only IPC.h */
#include "IPC.h"
/* note -- the above includes prototypes for the procedures below */
#include "exec/memory.h"
#include "exec/tasks.h"
struct Task * FindTask();
/*
* IPCBasePort is the node around which the whole IPC system is organized.
* It is placed in AmigaExec's public port list by the first program to
* use IPC, and remains there ever after. It is the ONLY public port
* created by the IPC system -- all IPCPorts are hung on a list from a
* header in the IPCBasePort structure. It may also be an actual port
* served by a "broker" process, which will be sent messages requesting,
* for example, that it load a server for a port.
*/
struct IPCBasePort *IPCBasePort = NULL;
/*
* OpenIPCBase
*
* returns a pointer to IPCBasePort, creating, initializing and
* adding it to the Exec public port list if it doesn't already exist.
* This procedure will normally only be called by other IPC system
* procedures (except possibly in a program such as a "broker").
*/
struct IPCBasePort * OpenIPCBase()
{
char * pname = "IPC_Base_Port";
struct MsgPort * port;
if (IPCBasePort) return IPCBasePort;
Forbid();
if (!(IPCBasePort = (struct IPCBasePort *)FindPort(pname))) {
IPCBasePort = (struct IPCBasePort *)
AllocMem(sizeof(struct IPCBasePort), MEMF_CLEAR | MEMF_PUBLIC);
if (IPCBasePort) {
port = (struct MsgPort *) IPCBasePort;
port->mp_Node.ln_Name = IPCBasePort->ipcb_Port.ipp_Name;
port->mp_Node.ln_Type = NT_MSGPORT;
AddPort(port);
NewList(&(IPCBasePort->ipcb_PortList));
strcpy(IPCBasePort->ipcb_Port.ipp_Name, pname);
}
}
Permit();
return IPCBasePort;
}
/*** There is NO CloseIPCBase()!! (it stays around forever) ***/
/*
* CreateIPCPort is a private procedure which should not be called
* directly by a user program. (Actually it is only called by GetIPCPort
* but for clarity it is kept separate.)
* The created IPCPort will be added to the list in IPCBasePort
* (GetIPCPort won't allow duplicate names) unless the name is NULL,
* in which case an "anonymous" port will be created that does not get
* onto the list; such anonymous ports can only be accessed by other
* processes if they are themselves passed as pointers in messages.
*/
static struct IPCPort * CreateIPCPort(name) char *name;
{
struct IPCPort * port;
int psize;
if (!OpenIPCBase()) return NULL; /* Quick check before we do anything */
psize = sizeof(struct IPCPort) + (name ? strlen(name) : 0);
/* psize is actually one byte too big -- do you care? */
port = (struct IPCPort *)
AllocMem(psize, MEMF_CLEAR | MEMF_PUBLIC);
if (port) {
port->ipp_Size = psize;
NewList(&(port->ipp_Port.mp_MsgList));
port->ipp_Port.mp_Node.ln_Type = NT_MSGPORT;
port->ipp_Port.mp_Flags = PA_IGNORE; /* initially */
if (name) { /* anonymous port is not put on list */
port->ipp_Port.mp_Node.ln_Name = port->ipp_Name;
/* point to name storage array */
strcpy(port->ipp_Name, name); /* move name to permanent storage */
AddHead(&IPCBasePort->ipcb_PortList, port);
}
}
return port;
}
/*
* FindIPCPort
*
* Finds the IPCPort with the name supplied as argument if
* it has been previously created.
* Returns pointer to port if it exists -- null otherwise;
* registers a new connection to the port (i.e. increments UseCount).
* (Connection must be terminated when program is done by the procedure
* DropIPCPort.)
* If the current server has set the IPP_NOTIFY flag in the port, the
* server task will be signalled using the port signal bit. NOTE that
* WaitPort will NOT detect these signals, because no message is
* actually sent; the program must do a Wait on this bit, and should do
* a GetMsg as usual, but if the message pointer is null it should
* then call, for example, CheckIPCPort.
*/
struct IPCPort * FindIPCPort(name) char *name;
{
struct IPCPort * port;
if (!OpenIPCBase()) return NULL; /* Quick check before we do anything */
if (!name) return NULL; /* port could be anonymous */
Forbid();
port = (struct IPCPort *)FindName(&IPCBasePort->ipcb_PortList, name);
if (port) port->ipp_UseCount++;
Permit();
if (port && port->ipp_Flags & IPP_NOTIFY)
/* Server wants to be notified */
Signal(port->ipp_Port.mp_SigTask,
1<<port->ipp_Port.mp_SigBit);
return port;
}
/*
* GetIPCPort
*
* Returns a pointer to IPCPort with the name supplied as an argument;
* unlike FindIPCPort, it always returns pointer to port -- this is
* created if it doesn't exist; registers a new connection to the port
* (use DropIPCPort when done). It will notify a server if requested
* (see FindIPCPort).
*/
struct IPCPort * GetIPCPort(name) char *name;
{
struct IPCPort * port;
Forbid();
port = FindIPCPort(name);
if (!port) {
port = CreateIPCPort(name);
port->ipp_UseCount++;
}
Permit();
return port;
}
/*
* UseIPCPort
*
* Registers another connection to a port (normally when the port
* was passed as a pointer from another process).
* (Use DropIPCPort when done.)
*/
void UseIPCPort(port) struct IPCPort * port;
{
port->ipp_UseCount++;
if (port->ipp_Flags & IPP_NOTIFY) /* Server wants to be notified */
Signal(port->ipp_Port.mp_SigTask,
1<<port->ipp_Port.mp_SigBit);
}
/*
* DropIPCPort
*
* Terminate a connection to a port established by FindIPCPort,
* GetIPCPort, or UseIPCPort. Port will be destroyed if there are
* no other connections left.
* If the IPP_NOTIFY flag is set in the port, the server will be
* signalled when this procedure is called (see FindIPCPort).
*/
void DropIPCPort(port) struct IPCPort * port;
{
if (!port) return; /* to save the client some trouble
(in a cleanup procedure) */
Forbid();
if (--port->ipp_UseCount == 0) {
/* an anonymous port is NOT on list -- ALL others MUST be! */
if (port->ipp_Port.mp_Node.ln_Name) Remove(port);
Permit();
FreeMem(port, port->ipp_Size);
}
else {
if (port->ipp_Flags & IPP_NOTIFY) /* Server wants to be notified */
Signal(port->ipp_Port.mp_SigTask,
1<<port->ipp_Port.mp_SigBit);
Permit();
}
}
/*
* ServeIPCPort
*
* Registers calling task as the server on the named port (which is
* created if it doesn't exist); null is returned if the port already
* has a server, otherwise a pointer to it is returned. At the same
* time the port is given the server as its SigTask and a suitable
* signal bit is allocated.
*/
struct IPCPort * ServeIPCPort(name) char *name;
{
struct IPCPort * port;
port = GetIPCPort(name);
if (port) {
Forbid();
if ((port->ipp_Flags & (IPP_SERVED | IPP_SHUT))
|| (port->ipp_Port.mp_SigBit = AllocSignal(-1)) == -1) {
DropIPCPort(port);
port = NULL;
}
else {
port->ipp_Port.mp_Flags = PA_SIGNAL;
port->ipp_Port.mp_SigTask = FindTask(0);
port->ipp_Flags |= IPP_SERVED;
}
Permit();
}
return port;
}
/*
* ShutIPCPort
*
* ONLY the current server may call this procedure.
* It prevents more messages being sent to this port, but
* does not end server connection; remaining messages can be dealt
* with before finally calling LeaveIPCPort.
* Note that it does NOT inhibit the server being signalled if
* IPP_NOTIFY is set and another client connects. (At the moment
* there is no mechanism to reopen a shut port without Leaving
* first; this may be possible in a future revision.)
*/
void ShutIPCPort(port) struct IPCPort * port;
{
port->ipp_Flags |= IPP_SHUT; /* Prevent other servers connecting */
port->ipp_Flags &= ~IPP_SERVED; /* prevent messages from landing */
port->ipp_Port.mp_Flags = PA_IGNORE; /* someone might use PutMsg! */
}
/*
* LeaveIPCPort
*
* ONLY the current server may call this procedure.
* Disconnects the server process from the port; another process
* can then become a server if it desires. If there are no other
* connections, the port is removed.
*/
void LeaveIPCPort(port) struct IPCPort * port;
{
FreeSignal(port->ipp_Port.mp_SigBit);
port->ipp_Port.mp_SigTask = NULL;
port->ipp_Flags &= ~(IPP_SHUT | IPP_SERVED | IPP_SERVER_FLAGS);
DropIPCPort(port);
}
/*
* CheckIPCPort
*
* Returns the number of current connections to this port (including
* the server); a call by the server (only) will also set the (user
* settable) port flags to the value in the second argument --
* currently the only valid flag is IPP_NOTIFY.
*/
CheckIPCPort(port, flags)
struct IPCPort *port;
USHORT flags;
{
if (port->ipp_Port.mp_SigTask == FindTask(0))
/* only server can change flags */
port->ipp_Flags = (port->ipp_Flags & ~IPP_SERVER_FLAGS) |
(flags & IPP_SERVER_FLAGS);
return (int) port->ipp_UseCount;
}
/*
* PutIPCMsg
*
* Sends an IPCMessage to an IPCPort; if the port has no server or
* is shut, the message is not sent and the function returns FALSE;
* otherwise it returns TRUE. (Other port flags to be added later
* may affect these actions.)
* Note that a ReplyPort should be supplied in the message as usual
* (except for the rare possible usage where a message is not to
* be replied; in this case, the IPC_TRANSFER flag must be set in
* both the message and all the items it contains, and the ReplyPort
* must be NULL).
*/
PutIPCMsg(port,msg)
struct IPCPort *port;
struct IPCMessage *msg;
{
Forbid(); /* we do this the straightforward way -- it's very quick */
if (port->ipp_Flags & IPP_SERVED) {
PutMsg(port, msg);
Permit();
return TRUE;
}
Permit();
return FALSE;
}
/*
* CreateIPCMsg
*
* Creates a standard IPCMessage block (in MEMF_PUBLIC) with the
* number of IPCItems supplied as argument. (Special cases -- like
* in-line data -- you will have to handle yourself, and you always
* have to manage the data blocks yourself).
*/
struct IPCMessage * CreateIPCMsg(nitems)
{
int msgsize;
struct IPCMessage * mp;
msgsize = sizeof(struct IPCMessage) +
(nitems -1)*sizeof(struct IPCItem);
mp = (struct IPCMessage *)
AllocMem(msgsize, MEMF_CLEAR | MEMF_PUBLIC);
if (mp) {
mp->ipc_Msg.mn_Length = msgsize - sizeof(struct Message);
mp->ipc_ItemCount = nitems;
}
return mp;
}
/*
* DeleteIPCMsg
*
* Deletes a standard IPCMessage block; you must first have disposed
* of any attached data as appropriate.
*/
void DeleteIPCMsg(msg) struct IPCMessage *msg;
{
FreeMem(msg, sizeof(struct Message) + msg->ipc_Msg.mn_Length);
}
/*********************************************/